package Controller;

import java.io.BufferedReader;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;

import javax.swing.JOptionPane;

import java.util.concurrent.locks.ReentrantLock;

import Model.*;
import View.MainFrame;

/**
 * enum type for group sorting
 * @author Vincent Francolin
 * @version 1.1 : add lock to prevent multiple access with save daemon
 * 			1.0 : creation
 */
enum Sort{
	DATE,ALPHA,GROUP
}

/**
 * Main controller of the application handle all data
 * 
 * @author Vincent Francolin
 * @version 1.0 : creation
 *  zip file : http://www.oracle.com/technetwork/articles/java/compress-1565076.html
 */
public final class ContactController {
	private ArrayList<ContactRecord> contactList;
	private ArrayList<ContactRecord> sortedContactList;
	private ArrayList<Group> groupList;
	private MainFrame view;
	private int currentSelected;
	private Sort sortType;
	
	private ReentrantLock contactLock = new ReentrantLock();
	private ReentrantLock groupLock = new ReentrantLock();

	/**
	 * default constructor
	 */
	public ContactController() {
		view = new MainFrame(this);
		contactList = new ArrayList<ContactRecord>();
		groupList = new ArrayList<Group>();
		sortedContactList = new ArrayList<ContactRecord>();
		currentSelected = -1;
		loadGroups();
		loadContacts();
		sortedContactList = (ArrayList<ContactRecord>) contactList.clone();
		for (ContactRecord obj : contactList) {
			view.addContactToView(obj.getContact().getName() + " "
					+ obj.getContact().getFirstName());
		}
		sortType = Sort.DATE;
	}

	/**
	 * create the context for the view
	 */
	public void createContext() {
		view.setVisible(true);
		view.pack();
	}

	/**
	 * get a contact entry
	 * 
	 * @param id
	 *            the id of the contact
	 * @return the contact entry
	 */
	public Contact getContact(int id) {
		//return contactList.get(id).getContact();
		if(sortedContactList.get(id)==null)
			return null;
		return sortedContactList.get(id).getContact();
	}
	
	/**
	 * get a contact record entry
	 * 
	 * @param id
	 *            the id of the contact
	 * @return the contact entry
	 */
	public ContactRecord getContactRecord(int id) {
		//return contactList.get(id).getContact();
		if(sortedContactList.get(id)==null)
			return null;
		return sortedContactList.get(id);
	}
	
	/**
	 * get the group of a contact entry
	 * 
	 * @param id
	 *            the id of the contact
	 * @return the contact entry
	 */
	public Group getContactGroup(int id) {
		//return contactList.get(id).getContact();
		if(sortedContactList.get(id)==null)
			return null;
		return sortedContactList.get(id).getGroup();
	}
	

	/**
	 * getter of the current selected contact
	 * 
	 * @return the id of selected contact
	 */
	public int getCurrentSelected() {
		return currentSelected;
	}

	/**
	 * set the current selected contact
	 * 
	 * @param currentSelected
	 *            the id of the contact
	 */
	public void setCurrentSelected(int currentSelected) {
		System.out.println("debug id : " + currentSelected);
		this.currentSelected = currentSelected;
	}

	/**
	 * save contacts into a file
	 * 
	 * @return true on success
	 */
	public boolean saveContacts() {
		contactLock.lock();
		String res = "";
		java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(
				"yyyy-MM-dd_HH.mm.ss");
		for (ContactRecord rc : contactList) {
			if (rc.saveContact())
				res += rc.getContact().getName() + ";"
						+ rc.getContact().getFirstName() + ";"
						+ sdf.format(rc.getAddDate()) + ";" +
						rc.getGroupName() + "\n";

		}
		String filename = "contactList";
		File file = new File(filename);
		try {
			if (!file.exists()) {
				file.createNewFile();
			}
			FileWriter fw = new FileWriter(file.getAbsoluteFile());
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(res);
			bw.close();
		} catch (Exception e) {
			System.out.println("error : " + e);
			JOptionPane.showMessageDialog(null, "Error : " + e);
			e.printStackTrace();
			contactLock.unlock();
			return false;
		}
		contactLock.unlock();
		return true;
	}

	/**
	 * load contact from files
	 * 
	 * @return true on success
	 */
	public boolean loadContacts() {
		contactLock.lock();
		String filename = "contactList";
		ArrayList<String> contactFiles = new ArrayList<String>();
		ArrayList<String> contactGroup = new ArrayList<String>();
		Group tmp;
		try {
			InputStream ips = new FileInputStream(filename);
			InputStreamReader ipsr = new InputStreamReader(ips);
			BufferedReader br = new BufferedReader(ipsr);
			String line;
			while ((line = br.readLine()) != null) {
				String[] content = line.split("[;]");
				contactFiles.add(new String(content[0] + "&" + content[1] + "&"
						+ content[2] + ".contact"));
				contactGroup.add(content[3]);
			}
			br.close();
			for (int i=0; i<contactFiles.size();i++) {
				tmp = null;
				if(contactGroup.get(i)!="null"){
					for(Group g : groupList){
						if(g.getName().equals(contactGroup.get(i))){
							tmp = g;
							System.out.println("debug, find a group!");
							break;
						}
					}
				}
				if(tmp!=null)
					System.out.println("group is : "+tmp.getName());
				contactList.add(new ContactRecord(contactFiles.get(i),tmp));
			}
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, "Error : " + e);
			System.out.println(e.toString());
			e.printStackTrace();
			contactLock.unlock();
			return false;
		}
		contactLock.unlock();
		return true;
	}

	/**
	 * save groups in file
	 * 
	 * @return true on success
	 */
	public boolean saveGroups() {
		groupLock.lock();
		String res = "";
		for (Group g : groupList) {
			res += g.getName() + ";";

		}
		String filename = "groupList";
		File file = new File(filename);
		try {
			if (!file.exists()) {
				file.createNewFile();
			}
			FileWriter fw = new FileWriter(file.getAbsoluteFile());
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(res);
			bw.close();
		} catch (Exception e) {
			System.out.println("error : " + e);
			JOptionPane.showMessageDialog(null, "Error : " + e);
			e.printStackTrace();
			groupLock.unlock();
			return false;
		}
		groupLock.unlock();
		return true;
	}

	/**
	 * load group from file
	 * 
	 * @return true on success
	 */
	public boolean loadGroups() {
		groupLock.lock();
		String filename = "groupList";
		try {
			InputStream ips = new FileInputStream(filename);
			InputStreamReader ipsr = new InputStreamReader(ips);
			BufferedReader br = new BufferedReader(ipsr);
			String line;
			while ((line = br.readLine()) != null) {
				String[] content = line.split("[;]");
				for (String obj : content) {
					System.out.println("Create group : "+obj);
					groupList.add(new Group(obj));
				}
			}
			br.close();
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, "Error : " + e);
			System.out.println(e.toString());
			e.printStackTrace();
			groupLock.unlock();
			return false;
		}
		groupLock.unlock();
		return true;
	}

	/**
	 * add a contact in the list
	 * 
	 * @param c
	 *            the contact data
	 * @param g
	 *            the group which the contact belongs to
	 * @return true on success
	 */
	public boolean addContact(Contact c, Group g) {
		contactLock.lock();
		view.addContactToView(c.getName() + " " + c.getFirstName());
		Date now = new Date();
		now = new Date(now.getTime());
		contactList.add(new ContactRecord(contactList.size(), c, g, now));
		
		//sortedContactList = (ArrayList<ContactRecord>) contactList.clone();
		switch(sortType){
		case DATE:
			view.updateEntireList(sortByDate());
			break;
		case ALPHA:
			view.updateEntireList(sortByAlphanumeric());
			break;
		case GROUP:
			view.updateEntireList(sortByGroup());
			break;
		}
		contactLock.unlock();
		return true;
	}
	
	/**
	 * add a contact in the list
	 * 
	 * @param c
	 *            the contact data
	 * @param g
	 *            the group which the contact belongs to
	 * @return true on success
	 */
	public boolean addContactFromFile(String filename) {
		contactLock.lock();
		Date now = new Date();
		now = new Date(now.getTime());
		ContactRecord rc = new ContactRecord(filename,null,now);
		view.addContactToView(rc.getContact().getName() + " " + rc.getContact().getFirstName());
		
		contactList.add(rc);
		
		//sortedContactList = (ArrayList<ContactRecord>) contactList.clone();
		switch(sortType){
		case DATE:
			view.updateEntireList(sortByDate());
			break;
		case ALPHA:
			view.updateEntireList(sortByAlphanumeric());
			break;
		case GROUP:
			view.updateEntireList(sortByGroup());
			break;
		}
		contactLock.unlock();
		return true;
	}

	/**
	 * remove a contact from the list
	 * 
	 * @param id
	 *            the id of the contact
	 * @return true on success
	 */
	public boolean removeContact(int id) {
		contactLock.lock();
		view.removeContactFromView(id);
		contactList.remove(id);
		switch(sortType){
		case DATE:
			view.updateEntireList(sortByDate());
			break;
		case ALPHA:
			view.updateEntireList(sortByAlphanumeric());
			break;
		case GROUP:
			view.updateEntireList(sortByGroup());
			break;
		}
		contactLock.unlock();
		return true;
	}

	/**
	 * update a contact entry
	 * 
	 * @param cnt
	 *            the new data
	 */
	public void updateCurrentContact(Contact cnt) {
		contactLock.lock();
		if (cnt != null) {
			contactList.get(currentSelected).setContact(cnt);
			view.updateContactInList(cnt.getName() + " " + cnt.getFirstName(),
					currentSelected);
		}
		contactLock.unlock();
	}

	/**
	 * method to save the context before exit
	 */
	public void saveContextBeforeExit() {
		System.out.println("debug quit");
		this.saveContacts();
		this.saveGroups();
		System.exit(0);
	}

	/**
	 * add a new group
	 * 
	 * @param name
	 *            the group's name
	 */
	public void addGroup(String name) {
		groupLock.lock();
		groupList.add(new Group(name));
		groupLock.unlock();
	}

	/**
	 * get the group list formatted in a string list
	 * 
	 * @return the group list in String object
	 */
	public String[] getGroupList() {
		String[] res = new String[groupList.size() + 1];
		res[0] = "none";
		for (int i = 0; i < groupList.size(); i++) {
			res[i + 1] = groupList.get(i).getName();
		}
		return res;
	}

	/**
	 * sort the list by creation date
	 * 
	 * @return an array of string which represent the sorted list
	 */
	public String[] sortByDate() {
		sortType = Sort.DATE;
		ArrayList<String> res = new ArrayList<String>();
		for (ContactRecord rc : contactList) {
			res.add(rc.getContact().getName() + " "
					+ rc.getContact().getFirstName());
		}
		String[] result = new String[res.size()];
		for(int i=0;i<res.size();i++){
			result[i] = res.get(i);
		}
		sortedContactList.clear();
		sortedContactList = (ArrayList<ContactRecord>) contactList.clone();
		
		return result;
	}

	/**
	 * sort the list by group which contact belongs to
	 * 
	 * @return an array of string which represent the sorted list
	 */
	public String[] sortByGroup() {
		sortType = Sort.GROUP;
		ArrayList<String> res = new ArrayList<String>();
		sortedContactList.clear();
		for (Group g : groupList) {
			res.add("<html><b>--- " + g.getName() + " ---</b></html>");
			sortedContactList.add(null);
			for (ContactRecord rc : contactList) {
				if (g.getName().equals(rc.getGroupName())) {
					res.add(rc.getContact().getName() + " "
							+ rc.getContact().getFirstName());
					sortedContactList.add(rc);
				}
			}
		}
		res.add("<html><b>--- No group ---</b></html>");
		sortedContactList.add(null);
		for (ContactRecord rc : contactList) {
			if (rc.getGroup() == null) {
				res.add(rc.getContact().getName() + " "
						+ rc.getContact().getFirstName());
				sortedContactList.add(rc);
			}
		}
		String[] result = new String[res.size()];
		for(int i=0;i<res.size();i++){
			result[i] = res.get(i);
		}
		return result;
	}

	/**
	 * sort the list by alpha numeric order
	 * 
	 * @return an array of string which represent the sorted list
	 */
	public String[] sortByAlphanumeric() {
		sortType = Sort.ALPHA;
		ArrayList<String> res = new ArrayList<String>();
		sortedContactList.clear();
		ArrayList<ContactRecord> copy = (ArrayList<ContactRecord>) contactList
				.clone();
		while (copy.size() != 0) {
			ContactRecord first = copy.get(0);
			for (ContactRecord obj : copy) {
				if (obj.getContact().getName()
						.compareTo(first.getContact().getName()) < 0)
					first = obj;
			}
			sortedContactList.add(first);
			res.add(first.getContact().getName() + " "
					+ first.getContact().getFirstName());
			copy.remove(first);
		}
		String[] result = new String[res.size()];
		for(int i=0;i<res.size();i++){
			result[i] = res.get(i);
		}
		return result;
	}
	
	/**
	 * get all the contact in list containing their name and first name
	 * @return arraylist of strings containing name and first name
	 */
	public ArrayList<String> getContactNames(){
		ArrayList<String> res = new ArrayList<String>();
		
		for(ContactRecord obj : contactList){
			res.add(obj.getContact().getName()+" "+obj.getContact().getFirstName());
		}
		return res;
	}
	
	/**
	 * clear all data
	 */
	public void resetAll(){
		contactList.clear();
		sortedContactList.clear();
		groupList.clear();
		currentSelected=-1;
		view.updateEntireList(null);
	}
	
}
